using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
using Object = UnityEngine.Object;

namespace LFromeWork
{
    public class AssetLoader : IDisposable
    {
        LinkedList<AssetRefEntity> exitAssets = null;
        LinkedList<AssetRefEntity> loadingAssets = null;

        public AssetLoader()
        {
            exitAssets = new LinkedList<AssetRefEntity>();
            loadingAssets = new LinkedList<AssetRefEntity>();
        }

        /// <summary>
        /// 异步加载asset AB包模式
        /// </summary>
        /// <param name="assetFullPath"></param>
        /// <param name="onLoadAssetComplete"></param>
        public async void LoadAssetAsync(string assetFullPath, Action<string, Object> onLoadAssetComplete)
        {
            //0.先判断是否已经 存在
            AssetRefEntity entity = GetAssetRefEntity(assetFullPath, exitAssets);
            if (entity != null)
            {
                entity.ReferenceAdd();
                onLoadAssetComplete?.Invoke(entity.AssetFullPath, entity.Target);
                return;
            }

            //1.检查是否已经正在加载
            entity = GetAssetRefEntity(assetFullPath, loadingAssets);

            //2.如果在任意一个队列中，添加引用计数
            if (entity != null)
            {
                entity.OnLoadAssetComplete += onLoadAssetComplete;
                entity.ReferenceAdd();
                return;
            }

            //3.不存在任何队列中的话创建
            entity = AssetRefEntity.Create(assetFullPath);
            entity.OnLoadAssetComplete += onLoadAssetComplete;
            entity.ReferenceAdd();
            loadingAssets.AddLast(entity);

            //4.加载 依赖ab 和 主ab
            AssetBundle m_MainAssetBundle = await LoadAssetBundleMainAndDependAsync(assetFullPath);

            //5. 真正异步加载资源
            entity.LoadAssetAsync(m_MainAssetBundle);
        }

        /// <summary>
        /// 加载ab 依赖
        /// </summary>
        /// <param name="assetFullName"></param>
        /// <returns></returns>
        private async Task<AssetBundle> LoadAssetBundleMainAndDependAsync(string assetFullName)
        {
            AssetInfoEntity assetEntity = GameLaunch.Resources.AssetInfo.GetAssetEntity(assetFullName);
            if (assetEntity == null) return null;

            //加载这个资源所依赖的资源包
            List<string> dependsAssetList = assetEntity.DependsAssetBundleList;
            for (int i = 0; i < dependsAssetList.Count; i++)
            {
                await GameLaunch.Resources.AssetBundleLoader.LoadAssetBundleAsync(dependsAssetList[i]);
            }

            //加载主资源包
            AssetBundle mainAssetBundle =
                await GameLaunch.Resources.AssetBundleLoader.LoadAssetBundleAsync(assetEntity.AssetBundleFullPath);
            if (mainAssetBundle == null)
            {
                GameLaunch.LogError("MainAssetBundle not exists " + assetEntity.AssetFullPath);
                return null;
            }

            return mainAssetBundle;
        }

        /// <summary>
        /// 释放 asset
        /// </summary>
        /// <param name="assetFullPath"></param>
        /// <param name="onLoadAssetComplete"></param>
        public void ReleaseAsset(string assetFullPath, Action<string, Object> onLoadAssetComplete)
        {
            //0.先判断是否已经 存在
            AssetRefEntity entity = GetAssetRefEntity(assetFullPath, exitAssets);

            //1.如果不在 检查是否在加载中
            if (entity == null)
                entity = GetAssetRefEntity(assetFullPath, loadingAssets);

            //3.引用计数减减
            if (entity != null)
            {
                entity.ReferenceRemove();
                entity.OnLoadAssetComplete -= onLoadAssetComplete;
            }
        }
        
        /// <summary>
        /// Editor模式
        /// </summary>
        /// <param name="assetFullPath"></param>
        /// <param name="onLoadAssetComplete"></param>
        public void LoadAssetEditor(string assetFullPath, Action<string, Object> onLoadAssetComplete)
        {
            //0.先判断是否已经 存在
            AssetRefEntity entity = GetAssetRefEntity(assetFullPath, exitAssets);
            if (entity != null)
            {
                entity.ReferenceAdd();
                onLoadAssetComplete?.Invoke(entity.AssetFullPath, entity.Target);
                return;
            }
            
            //3.不存在任何队列中的话创建
            entity = AssetRefEntity.Create(assetFullPath);
            entity.ReferenceAdd();
            exitAssets.AddLast(entity);
#if UNITY_EDITOR
            entity.Target = UnityEditor.AssetDatabase.LoadAssetAtPath<Object>(assetFullPath);
#endif
            onLoadAssetComplete?.Invoke(assetFullPath,entity.Target);
        }
        
        /// <summary>
        /// Editor模式
        /// </summary>
        /// <param name="assetFullPath"></param>
        /// <param name="onLoadAssetComplete"></param>
        public void ReleaseAssetEditor(string assetFullPath)
        {
            //0.先判断是否已经 存在
            AssetRefEntity entity = GetAssetRefEntity(assetFullPath, exitAssets);
            //1.引用计数减减
            if (entity != null)
                entity.ReferenceRemove();
        }

        /// <summary>
        /// 更具 assetFullPath 获取 AssetRefEntity
        /// </summary>
        /// <param name="assetFullPath"></param>
        /// <param name="list"></param>
        /// <returns></returns>
        private AssetRefEntity GetAssetRefEntity(string assetFullPath, LinkedList<AssetRefEntity> list)
        {
            for (LinkedListNode<AssetRefEntity> curr = list.First; curr != null; curr = curr.Next)
            {
                if (curr.Value.AssetFullPath == assetFullPath)
                {
                    return curr.Value;
                }
            }

            return null;
        }
        
        /// <summary>
        /// 获取已经加载好的 assetRefEntity
        /// </summary>
        /// <param name="assetFullPath"></param>
        /// <returns></returns>
        public AssetRefEntity GetExitAssetRefEntity(string assetFullPath)
        {
            for (LinkedListNode<AssetRefEntity> curr = exitAssets.First; curr != null; curr = curr.Next)
            {
                if (curr.Value.AssetFullPath == assetFullPath)
                {
                    return curr.Value;
                }
            }
            return null;
        }

        /// <summary>
        /// 检查 asset 异步加载状态
        /// </summary>
        public void OnUpdate()
        {
            //检查加载是否完成
            CheckLoadingAssetStatus();

            //检查是否可释放
            CheckExitAssetStatus();
        }

        /// <summary>
        /// 检查加载中ab 的状态
        /// </summary>
        private void CheckLoadingAssetStatus()
        {
            LinkedListNode<AssetRefEntity> currLoad = loadingAssets.First;
            LinkedListNode<AssetRefEntity> next = null;
            while (currLoad != null)
            {
                if (currLoad.Value.CurrAssetBundleRequest != null && currLoad.Value.CurrAssetBundleRequest.isDone)
                {
                    next = currLoad.Next;
                    loadingAssets.Remove(currLoad.Value);
                    exitAssets.AddLast(currLoad.Value);
                    currLoad.Value.UpdateAssetBundleRequest();
                    currLoad = next;
                }
                else
                    currLoad = currLoad.Next;
            }
        }

        /// <summary>
        /// 检查已经加载好的ab状态
        /// </summary>
        private void CheckExitAssetStatus()
        {
            LinkedListNode<AssetRefEntity> currExit = exitAssets.First;
            LinkedListNode<AssetRefEntity> next = null;
            while (currExit != null)
            {
                if (currExit.Value.GetCanRelease())
                {
                    GameLaunch.Pool.GoPool.ReleasePool(currExit.Value.AssetFullPath);
                    currExit.Value.Release();
                    next = currExit.Next;
                    exitAssets.Remove(currExit.Value);
                    currExit = next;
                }
                else
                    currExit = currExit.Next;
            }
        }

        public void Dispose()
        {
            exitAssets.Clear();
            exitAssets = null;
            loadingAssets.Clear();
            loadingAssets = null;
        }
    }
}
